package com.aizuda.encrypt.interceptor;

import cn.hutool.core.util.StrUtil;
import com.aizuda.encrypt.config.EncryptProperties;
import com.aizuda.encrypt.constant.EncryptConstant;
import com.aizuda.encrypt.handler.EncryptPreParameterRewriter;
import com.aizuda.encrypt.util.ParseSQLUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.sql.Statement;
import java.util.List;
import java.util.Properties;

/**
 * Mybatis拦截器 - 保存数据加密拦截
 *
 * @author nn200433
 * @date 2024-03-25 10:58:26
 */
@Slf4j
@Intercepts({@Signature(type = StatementHandler.class, method = "parameterize", args = Statement.class)})
public class EncryptSaveInterceptor implements Interceptor {

    private final EncryptProperties           cfg;
    private final EncryptPreParameterRewriter rewriter;

    public EncryptSaveInterceptor(EncryptProperties properties) {
        cfg      = properties;
        rewriter = new EncryptPreParameterRewriter(properties);
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        final Object          realTarget     = ParseSQLUtil.getRealTarget(invocation.getTarget());
        final MetaObject      metaObject     = SystemMetaObject.forObject(realTarget);
        final MappedStatement ms             = (MappedStatement) metaObject.getValue(EncryptConstant.MAPPED_STATEMENT_KEY);
        final SqlCommandType  sqlCommandType = ms.getSqlCommandType();
        if (sqlCommandType == SqlCommandType.INSERT || sqlCommandType == SqlCommandType.UPDATE) {
            final String sql = (String) metaObject.getValue(EncryptConstant.SQL_KEY);
            // "sys_operation_log", "alter table"
            if (StrUtil.containsAnyIgnoreCase(sql, cfg.getIgnoreEncryptSqlKeyWords())) {
                return invocation.proceed();
            }
            List<ParameterMapping> parameterMappings = (List<ParameterMapping>) metaObject.getValue(EncryptConstant.PARAMETER_MAPPINGS_KEY);
            Object                 parameterObject   = metaObject.getValue(EncryptConstant.PARAMETER_OBJECT_KEY);
            rewriter.rewrite(sql, sqlCommandType, parameterMappings, parameterObject);
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }

}
